A look at the latest Jedlovec House eletricity usage and solar production from PPL and Enphase.

Load packages

library(tidyverse)
library(lubridate)
library(hms)
library(readxl)

Load PPL data

Transform PPL Data


hourly_ppl_pivot <- ppl_15mins %>% 
  rename(date = Date) %>% 
  pivot_longer(!c("Account Number", "Meter Number", date, "Read Type", Min, Max, Total), names_to = "time", values_to = "kWh") 

rename(ppl_15mins, date = Date)

hourly_ppl_pivot <- hourly_ppl_pivot %>% 
  mutate(time = parse_time(time, '%H:%M %p'), month = month(date, label=TRUE), year = year(date), yday = yday(date), wday = wday(date, label=TRUE))

(hourly_ppl_net <- hourly_ppl_pivot %>% 
  filter(`Read Type` == "kWh Net"))

Load Enphase data

import <- read_csv("enphase_history_230701.csv")
Rows: 42432 Columns: 2── Column specification ────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (1): Date/Time
dbl (1): Energy Produced (Wh)
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
update <- read_csv("hourly_generation_230625_240510.csv")
Rows: 30816 Columns: 2── Column specification ────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (1): Date/Time
dbl (1): Energy Produced (Wh)
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
update <- update %>% 
  mutate(`Date/Time` = as.POSIXct(`Date/Time`,format="%m/%d/%Y %H:%M",tz=Sys.timezone())) %>% 
  filter(`Date/Time` >= as.POSIXct('07/01/2023 00:00',format="%m/%d/%Y %H:%M",tz=Sys.timezone()) )

import <- import %>% 
  mutate(`Date/Time` = as.POSIXct(`Date/Time`,format="%m/%d/%Y %H:%M",tz=Sys.timezone())) 

full_hist <- rbind(import,update)

full_hist %>% arrange(desc(`Date/Time`))

(hourly_production <- full_hist %>% 
  rename(datetime = `Date/Time`, energy_produced_Wh = `Energy Produced (Wh)`) %>% 
  mutate(datetime = as.POSIXct(datetime,format="%m/%d/%Y %H:%M",tz=Sys.timezone())) %>% 
  mutate(date = date(datetime), time = as.hms(format(datetime, format = "%H:%M:%S")), month = month(datetime, label=TRUE), year = year(datetime), day = day(datetime), yday = yday(datetime), monthday = format(datetime, "%m-%d"), wday = wday(datetime, label=TRUE), equinox_day = (yday + 10) %% 365, equinox_group = floor((equinox_day+15)/30)*30)
)
NA

Spot-check Enphase Should see lifetime production by day and by hour

ggplot(hourly_production, aes(datetime, energy_produced_Wh)) +
  geom_point()


ggplot(hourly_production, aes(time, energy_produced_Wh)) +
  theme(axis.text.x = element_text(angle = 90)) +
  geom_point()

Net + Produced = Consumed

Calculate production for first year of solar panels, second year, etc.


hourly_electricity <- hourly_electricity %>% 
  mutate(solar_year = factor(case_when(
                        datetime < as.POSIXct('04/16/2022 00:00',format="%m/%d/%Y %H:%M",tz=Sys.timezone()) ~ 0,
                        datetime >= as.POSIXct('04/16/2022 00:00',format="%m/%d/%Y %H:%M",tz=Sys.timezone())  &
                          datetime < as.POSIXct('04/16/2023 00:00',format="%m/%d/%Y %H:%M",tz=Sys.timezone()) ~ 1,
                        datetime >= as.POSIXct('04/16/2023 00:00',format="%m/%d/%Y %H:%M",tz=Sys.timezone())  &
                          datetime < as.POSIXct('04/16/2024 00:00',format="%m/%d/%Y %H:%M",tz=Sys.timezone()) ~ 2,
                        TRUE ~ 3)
         )) %>% 
  mutate(yday = yday(date))

hourly_electricity %>% 
  group_by(solar_year) %>% 
  summarize(net_kWh = sum(net_kWh), produced_kWh = sum(produced_kWh), consumed_kWh = sum(consumed_kWh))
NA

Interesting! Produced kWh went down by 5%, but consumed kWh went down by 10% despite the fact that we got an electric car! Let’s explore that further.

daily_electricity <- hourly_electricity %>% 
  group_by(solar_year, yday) %>% 
  summarize(net_kWh = sum(net_kWh), produced_kWh = sum(produced_kWh), consumed_kWh = sum(consumed_kWh))
`summarise()` has grouped output by 'solar_year'. You can override using the `.groups` argument.
ggplot(daily_electricity, aes(yday, consumed_kWh, group=solar_year, color=solar_year)) + 
  geom_point() +
  geom_smooth(span=0.3) +
  scale_x_continuous(breaks = c(1, 32, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335), labels = month.abb) +
  theme(axis.text.x = element_text(angle = 90)) 

Look how our electricity consumption got much less predictable, more uneven, after we purchased an EV and installed a level 2 charger in late January/early February 2023!

We probably need to filter out the EV charging and compare that separately.


hourly_electricity %>% summarize(min_date = min(date), max_date = max(date))

electricity_by_time <- hourly_electricity %>% 
  filter(solar_year == 1 | solar_year == 2) %>% 
  #filter(date <= as.POSIXct('12/31/2022 00:00',format="%m/%d/%Y %H:%M",tz=Sys.timezone()) ) %>% 
  group_by(time, solar_year) %>% 
  summarize(produced_kWh = mean(produced_kWh), consumed_kWh = mean(consumed_kWh), net_kWh = mean(net_kWh)) %>% 
  arrange(time)
`summarise()` has grouped output by 'time'. You can override using the `.groups` argument.
electricity_by_time

ggplot(electricity_by_time, aes(x=time, y=consumed_kWh, group=solar_year, color=solar_year)) +
  geom_point() 

  #labs(colour="",x="Time of Day",y="Electricity Consumption (kWh)")+
  #scale_color_manual(values = c("red","black","green")) +
  #ggtitle("Home Electricity in 15-Minute Intervals (Since April 15, 2022)")

Wow, look how much less was used during the day in year 2!!! Like a 50% reduction!

Is this due to milder weather?

Let’s look at it by month:

#electricity_by_month <- 
hourly_electricity %>% 
  mutate(month = month(date)) %>% 
  group_by(solar_year, month) %>% 
  summarize(consumed_kWh = mean(consumed_kWh)) %>% 
  arrange(month, solar_year) %>% 
  pivot_wider(names_from = month, values_from = consumed_kWh) %>% 
  arrange(solar_year)
`summarise()` has grouped output by 'solar_year'. You can override using the `.groups` argument.
#Look at limited window during day (no EV charging)
 
# hourly_electricity %>% 
#   filter(time == '12:00:00') %>% 
#   #filter(time > as.POSIXct('04:00:00',format="%H:%M:$s",tz=Sys.timezone()) ) %>%  #& time < as.POSIXct('18:00',format="%H:%M",tz=Sys.timezone())) %>% 
#   mutate(month = month(date)) %>% 
#   group_by(solar_year, month) %>% 
#   summarize(consumed_kWh = mean(consumed_kWh)) %>% 
#   arrange(month, solar_year) %>% 
#   pivot_wider(names_from = month, values_from = consumed_kWh) %>% 
#   arrange(solar_year)

Look at specific months

electricity_by_month_time <- hourly_electricity %>% 
  mutate(month = as_factor(month(date))) %>% 
  filter(solar_year == 1 | solar_year == 2) %>% 
  filter(month == 10) %>% 
  group_by(solar_year, month, time) %>% 
  summarize(produced_kWh = mean(produced_kWh), consumed_kWh = mean(consumed_kWh), net_kWh = mean(net_kWh)) %>% 
  arrange(solar_year, month, time)
`summarise()` has grouped output by 'solar_year', 'month'. You can override using the `.groups` argument.
ggplot(electricity_by_month_time, aes(time, consumed_kWh, group=solar_year, color=solar_year)) +
  #facet_grid(rows = vars(month)) +
  geom_point(size=0.5) +
  ggtitle("Home Electricity in 15-Minute Intervals") +
  ylab("Net Electricity Consumption (kWh)")

Example day with EV charging

sample_day <- hourly_electricity %>% 
  filter(solar_year == 1 | solar_year == 2) %>% 
  filter(yday == 157) %>% 
  group_by(date, solar_year, yday, time) %>% 
  summarize(produced_kWh = mean(produced_kWh), consumed_kWh = mean(consumed_kWh), net_kWh = mean(net_kWh)) %>% 
  arrange(date, solar_year, yday, time)
`summarise()` has grouped output by 'date', 'solar_year', 'yday'. You can override using the `.groups` argument.
ggplot(sample_day, aes(time, consumed_kWh, group=solar_year, color=solar_year)) +
  #facet_grid(rows = vars(month)) +
  geom_point(size=0.5) +
  ggtitle("Home Electricity in 15-Minute Intervals") +
  ylab("Electricity Consumption (kWh)")

Ok, let’s detect and remove EV charging sessions.

bcp
$x
 [1]  2.38157896  2.49495792  2.35871228  2.67047501  2.71991881  2.55883013  2.33772138  2.91051260
 [9]  2.19237889  2.74960191  1.75213595 -0.01838713  0.30820336  0.39897059  0.28664757  0.30603608
[17]  0.35919098  0.16767281  0.21823037  0.43193225  0.33928205  0.39538327  0.39027328  0.25107071
[25]  0.21501386  0.12773723  0.07036768  0.02051450  0.03319477  0.18027518  0.33188973  0.38038835
[33]  0.13785630  0.42017572  0.22867892  0.24044121  0.23030774  0.28537113  0.51695711  0.29266042
[41] -0.06083516  0.06660989  0.31487811  0.14387669  0.39981560  0.30094562  0.09083242  0.35589074
[49]  0.04101847  0.13585666  0.01485267  0.42753704  0.27042197  0.09039031  0.52307131  0.18050086
[57]  0.28360934  0.71996580  0.20057504  0.35998894  0.47077430  0.21099498  0.32290440  0.14337854
[65]  0.29055250 -0.07771297  0.31868915  0.43214999  0.21821496  0.20267917  0.22137171  0.19991720
[73]  0.15276440  0.48992731  0.27547768  0.13486293  0.19747790  0.15852249  0.03325269  0.15447847
[81] -0.02821081  0.06667815 -0.20491370  0.13184547  0.08786327  0.25226203  0.02591429  0.24308958
[89]  0.37162490  0.15715362  0.12179673 -0.14603414  0.20990544  0.38083345  0.02071311  0.26131271
[97]  0.07817865

$max_p
           [,1] [,2] [,3]
 [1,] 0.9580194    0    0
 [2,] 0.9364674    0    1
 [3,] 0.9291162    0    2
 [4,] 0.9265527    0    3
 [5,] 0.9265851    0    4
 [6,] 0.9267314    0    5
 [7,] 0.9319284    0    6
 [8,] 0.9302070    0    7
 [9,] 0.9382416    0    8
[10,] 0.9188706    0    9
[11,] 0.4892223    0   10
[12,] 0.6201123   11    0
[13,] 0.8095788   11    1
[14,] 0.7979013   11    2
[15,] 0.7929583   11    3
[16,] 0.7921342   11    4
[17,] 0.7971379   11    5
[18,] 0.8047930   11    6
[19,] 0.8080573   11    7
[20,] 0.8163079   11    8
[21,] 0.8221754   11    9
[22,] 0.8283082   11   10
[23,] 0.8366701   11   11
[24,] 0.8438057   11   12
[25,] 0.8481012   11   13
[26,] 0.8504027   11   14
[27,] 0.8507848   11   15
[28,] 0.8547635   11   16
[29,] 0.8662926   11   17
[30,] 0.8722353   11   18
[31,] 0.8742465   11   19
[32,] 0.8783681   11   20
[33,] 0.8776178   11   21
[34,] 0.8835105   11   22
[35,] 0.8870574   11   23
[36,] 0.8896860   11   24
[37,] 0.8914833   11   25
[38,] 0.8796208   11   26
[39,] 0.8882480   11   27
[40,] 0.8727340   11   28
[41,] 0.8822929   11   29
[42,] 0.8917103   11   30
[43,] 0.8947029   11   31
[44,] 0.8936474   11   32
[45,] 0.8971827   11   33
[46,] 0.8957289   11   34
[47,] 0.8977034   11   35
[48,] 0.8935607   11   36
[49,] 0.8989002   11   37
[50,] 0.8934545   11   38
[51,] 0.8949324   11   39
[52,] 0.9020641   11   40
[53,] 0.9011736   11   41
[54,] 0.8869148   11   42
[55,] 0.8999069   11   43
[56,] 0.9039034   11   44
[57,] 0.8233297   11   45
[58,] 0.8803324   11   46
[59,] 0.8933737   11   47
[60,] 0.8894446   11   48
[61,] 0.8983125   11   49
[62,] 0.9004938   11   50
[63,] 0.9002783   11   51
[64,] 0.9025502   11   52
[65,] 0.8758334   11   53
[66,] 0.8969529   11   54
[67,] 0.8960692   11   55
[68,] 0.9020904   11   56
[69,] 0.9038043   11   57
[70,] 0.9048967   11   58
[71,] 0.9052346   11   59
[72,] 0.9045400   11   60
[73,] 0.8937625   11   61
[74,] 0.9026525   11   62
[75,] 0.9025963   11   63
[76,] 0.9050601   11   64
[77,] 0.9050209   11   65
[78,] 0.8969872   11   66
[79,] 0.9041006   11   67
[80,] 0.8884811   11   68
[81,] 0.8971434   11   69
[82,] 0.8047584   11   70
[83,] 0.8785588   11   71
[84,] 0.8964911   11   72
[85,] 0.9082148   11   73
[86,] 0.9023057   11   74
[87,] 0.9110744   11   75
[88,] 0.9091609   11   76
[89,] 0.9118557   11   77
[90,] 0.9116388   11   78
[91,] 0.8629416   11   79
[92,] 0.9032366   11   80
[93,] 0.9069030   11   81
[94,] 0.9035397   11   82
[95,] 0.9127871   11   83
[96,] 0.9111167   11   84
[97,] 0.0000000    0    0

$parameters
theta alpha  beta th_cp 
  0.9   1.0   1.0   0.5 

$series_length
[1] 97

$result
NULL

attr(,"class")
[1] "BayesCP"
plot(summary(bcp))
[1] "Change points"
[1] "Segments"
     begin end      mean         SD  LL of CI  UL of CI
[1,]     1  11 2.5354545 0.31740711 2.3780390 2.6928701
[2,]    12  96 0.1314471 0.09968958 0.1136615 0.1492326

LS0tCnRpdGxlOiAiRWxlY3RyaWNpdHkvU29sYXIgVXBkYXRlIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpBIGxvb2sgYXQgdGhlIGxhdGVzdCBKZWRsb3ZlYyBIb3VzZSBlbGV0cmljaXR5IHVzYWdlIGFuZCBzb2xhciBwcm9kdWN0aW9uIGZyb20gUFBMIGFuZCBFbnBoYXNlLgoKTG9hZCBwYWNrYWdlcwpgYGB7cn0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkobHVicmlkYXRlKQpsaWJyYXJ5KGhtcykKbGlicmFyeShyZWFkeGwpCmxpYnJhcnkob25saW5lQmNwKQpgYGAKCkxvYWQgUFBMIGRhdGEKYGBge3J9Cgpjb2xfZGF0YXR5cGVzIDwtIGMoJ251bWVyaWMnLCdudW1lcmljJywnZGF0ZScsJ3RleHQnLHJlcCgnbnVtZXJpYycsOTkpKQoKaG91cmx5MSA8LSByZWFkX2V4Y2VsKCJIb3VybHkgVXNhZ2UgMjIwMzI2IHRvIDIyMDYyNC54bHN4IiwgY29sX3R5cGVzID0gY29sX2RhdGF0eXBlcykKaG91cmx5MiA8LSByZWFkX2V4Y2VsKCJIb3VybHkgVXNhZ2UgMjIwNjI1IHRvIDIzMDYyMy54bHN4IiwgY29sX3R5cGVzID0gY29sX2RhdGF0eXBlcykKaG91cmx5MyA8LSByZWFkX2V4Y2VsKCJQUEwgMjMwNjI0IHRvIDI0MDUwOS54bHN4IiwgY29sX3R5cGVzID0gY29sX2RhdGF0eXBlcykKCiNob3VybHkxCiNob3VybHkyCiNob3VybHkzCgpwcGxfMTVtaW5zIDwtIGJpbmRfcm93cyhob3VybHkxLGxpc3QoaG91cmx5Mixob3VybHkzKSkKCnBwbF8xNW1pbnMgJT4lIGFycmFuZ2UoZGVzYyhEYXRlKSwgYFJlYWQgVHlwZWApCgoKYGBgCgpUcmFuc2Zvcm0gUFBMIERhdGEKYGBge3J9Cgpob3VybHlfcHBsX3Bpdm90IDwtIHBwbF8xNW1pbnMgJT4lIAogIHJlbmFtZShkYXRlID0gRGF0ZSkgJT4lIAogIHBpdm90X2xvbmdlcighYygiQWNjb3VudCBOdW1iZXIiLCAiTWV0ZXIgTnVtYmVyIiwgZGF0ZSwgIlJlYWQgVHlwZSIsIE1pbiwgTWF4LCBUb3RhbCksIG5hbWVzX3RvID0gInRpbWUiLCB2YWx1ZXNfdG8gPSAia1doIikgCgpyZW5hbWUocHBsXzE1bWlucywgZGF0ZSA9IERhdGUpCgpob3VybHlfcHBsX3Bpdm90IDwtIGhvdXJseV9wcGxfcGl2b3QgJT4lIAogIG11dGF0ZSh0aW1lID0gcGFyc2VfdGltZSh0aW1lLCAnJUg6JU0gJXAnKSwgbW9udGggPSBtb250aChkYXRlLCBsYWJlbD1UUlVFKSwgeWVhciA9IHllYXIoZGF0ZSksIHlkYXkgPSB5ZGF5KGRhdGUpLCB3ZGF5ID0gd2RheShkYXRlLCBsYWJlbD1UUlVFKSkKCihob3VybHlfcHBsX25ldCA8LSBob3VybHlfcHBsX3Bpdm90ICU+JSAKICBmaWx0ZXIoYFJlYWQgVHlwZWAgPT0gImtXaCBOZXQiKSkKYGBgCgpMb2FkIEVucGhhc2UgZGF0YQpgYGB7cn0KaW1wb3J0IDwtIHJlYWRfY3N2KCJlbnBoYXNlX2hpc3RvcnlfMjMwNzAxLmNzdiIpCnVwZGF0ZSA8LSByZWFkX2NzdigiaG91cmx5X2dlbmVyYXRpb25fMjMwNjI1XzI0MDUxMC5jc3YiKQoKdXBkYXRlIDwtIHVwZGF0ZSAlPiUgCiAgbXV0YXRlKGBEYXRlL1RpbWVgID0gYXMuUE9TSVhjdChgRGF0ZS9UaW1lYCxmb3JtYXQ9IiVtLyVkLyVZICVIOiVNIix0ej1TeXMudGltZXpvbmUoKSkpICU+JSAKICBmaWx0ZXIoYERhdGUvVGltZWAgPj0gYXMuUE9TSVhjdCgnMDcvMDEvMjAyMyAwMDowMCcsZm9ybWF0PSIlbS8lZC8lWSAlSDolTSIsdHo9U3lzLnRpbWV6b25lKCkpICkKCmltcG9ydCA8LSBpbXBvcnQgJT4lIAogIG11dGF0ZShgRGF0ZS9UaW1lYCA9IGFzLlBPU0lYY3QoYERhdGUvVGltZWAsZm9ybWF0PSIlbS8lZC8lWSAlSDolTSIsdHo9U3lzLnRpbWV6b25lKCkpKSAKCmZ1bGxfaGlzdCA8LSByYmluZChpbXBvcnQsdXBkYXRlKQoKZnVsbF9oaXN0ICU+JSBhcnJhbmdlKGRlc2MoYERhdGUvVGltZWApKQoKKGhvdXJseV9wcm9kdWN0aW9uIDwtIGZ1bGxfaGlzdCAlPiUgCiAgcmVuYW1lKGRhdGV0aW1lID0gYERhdGUvVGltZWAsIGVuZXJneV9wcm9kdWNlZF9XaCA9IGBFbmVyZ3kgUHJvZHVjZWQgKFdoKWApICU+JSAKICBtdXRhdGUoZGF0ZXRpbWUgPSBhcy5QT1NJWGN0KGRhdGV0aW1lLGZvcm1hdD0iJW0vJWQvJVkgJUg6JU0iLHR6PVN5cy50aW1lem9uZSgpKSkgJT4lIAogIG11dGF0ZShkYXRlID0gZGF0ZShkYXRldGltZSksIHRpbWUgPSBhcy5obXMoZm9ybWF0KGRhdGV0aW1lLCBmb3JtYXQgPSAiJUg6JU06JVMiKSksIG1vbnRoID0gbW9udGgoZGF0ZXRpbWUsIGxhYmVsPVRSVUUpLCB5ZWFyID0geWVhcihkYXRldGltZSksIGRheSA9IGRheShkYXRldGltZSksIHlkYXkgPSB5ZGF5KGRhdGV0aW1lKSwgbW9udGhkYXkgPSBmb3JtYXQoZGF0ZXRpbWUsICIlbS0lZCIpLCB3ZGF5ID0gd2RheShkYXRldGltZSwgbGFiZWw9VFJVRSksIGVxdWlub3hfZGF5ID0gKHlkYXkgKyAxMCkgJSUgMzY1LCBlcXVpbm94X2dyb3VwID0gZmxvb3IoKGVxdWlub3hfZGF5KzE1KS8zMCkqMzApCikKCmBgYAoKU3BvdC1jaGVjayBFbnBoYXNlClNob3VsZCBzZWUgbGlmZXRpbWUgcHJvZHVjdGlvbiBieSBkYXkgYW5kIGJ5IGhvdXIKYGBge3J9CmdncGxvdChob3VybHlfcHJvZHVjdGlvbiwgYWVzKGRhdGV0aW1lLCBlbmVyZ3lfcHJvZHVjZWRfV2gpKSArCiAgZ2VvbV9wb2ludCgpCgpnZ3Bsb3QoaG91cmx5X3Byb2R1Y3Rpb24sIGFlcyh0aW1lLCBlbmVyZ3lfcHJvZHVjZWRfV2gpKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCkpICsKICBnZW9tX3BvaW50KCkKCmBgYAoKTmV0ICsgUHJvZHVjZWQgPSBDb25zdW1lZAoKYGBge3J9CiMgaG91cmx5X3BwbF9uZXQgPC0gaG91cmx5X3BwbF9uZXQgJT4lIG11dGF0ZShkYXRlID0gYXNfZGF0ZShkYXRlKSkKCiNob3VybHlfcHBsX25ldCAlPiUgYXJyYW5nZShkZXNjKGRhdGUpKQojaG91cmx5X3Byb2R1Y3Rpb24gJT4lIGFycmFuZ2UoZGVzYyhkYXRlKSkKCihob3VybHlfZWxlY3RyaWNpdHkgPC0gaG91cmx5X3BwbF9uZXQgJT4lIAogICAgaW5uZXJfam9pbihob3VybHlfcHJvZHVjdGlvbiwgYnkgPSBqb2luX2J5KGRhdGUsdGltZSkpICAlPiUgCiAgICBtdXRhdGUoY29uc3VtZWRfa1doID0ga1doICsgZW5lcmd5X3Byb2R1Y2VkX1doLzEwMDAsIHByb2R1Y2VkX2tXaCA9IGVuZXJneV9wcm9kdWNlZF9XaC8xMDAwKSAgJT4lIAogICAgcmVuYW1lKG5ldF9rV2ggPSBrV2gpICU+JSAKICAgIHNlbGVjdChkYXRldGltZSwgZGF0ZSwgdGltZSwgbmV0X2tXaCwgcHJvZHVjZWRfa1doLCBjb25zdW1lZF9rV2gpICU+JQogICAgYXJyYW5nZShkYXRlKSkKCmBgYAoKQ2FsY3VsYXRlIHByb2R1Y3Rpb24gZm9yIGZpcnN0IHllYXIgb2Ygc29sYXIgcGFuZWxzLCBzZWNvbmQgeWVhciwgZXRjLgoKYGBge3J9Cgpob3VybHlfZWxlY3RyaWNpdHkgPC0gaG91cmx5X2VsZWN0cmljaXR5ICU+JSAKICBtdXRhdGUoc29sYXJfeWVhciA9IGZhY3RvcihjYXNlX3doZW4oCiAgICAgICAgICAgICAgICAgICAgICAgIGRhdGV0aW1lIDwgYXMuUE9TSVhjdCgnMDQvMTYvMjAyMiAwMDowMCcsZm9ybWF0PSIlbS8lZC8lWSAlSDolTSIsdHo9U3lzLnRpbWV6b25lKCkpIH4gMCwKICAgICAgICAgICAgICAgICAgICAgICAgZGF0ZXRpbWUgPj0gYXMuUE9TSVhjdCgnMDQvMTYvMjAyMiAwMDowMCcsZm9ybWF0PSIlbS8lZC8lWSAlSDolTSIsdHo9U3lzLnRpbWV6b25lKCkpICAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0ZXRpbWUgPCBhcy5QT1NJWGN0KCcwNC8xNi8yMDIzIDAwOjAwJyxmb3JtYXQ9IiVtLyVkLyVZICVIOiVNIix0ej1TeXMudGltZXpvbmUoKSkgfiAxLAogICAgICAgICAgICAgICAgICAgICAgICBkYXRldGltZSA+PSBhcy5QT1NJWGN0KCcwNC8xNi8yMDIzIDAwOjAwJyxmb3JtYXQ9IiVtLyVkLyVZICVIOiVNIix0ej1TeXMudGltZXpvbmUoKSkgICYKICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRldGltZSA8IGFzLlBPU0lYY3QoJzA0LzE2LzIwMjQgMDA6MDAnLGZvcm1hdD0iJW0vJWQvJVkgJUg6JU0iLHR6PVN5cy50aW1lem9uZSgpKSB+IDIsCiAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiAzKQogICAgICAgICApKSAlPiUgCiAgbXV0YXRlKHlkYXkgPSB5ZGF5KGRhdGUpKQoKaG91cmx5X2VsZWN0cmljaXR5ICU+JSAKICBncm91cF9ieShzb2xhcl95ZWFyKSAlPiUgCiAgc3VtbWFyaXplKG5ldF9rV2ggPSBzdW0obmV0X2tXaCksIHByb2R1Y2VkX2tXaCA9IHN1bShwcm9kdWNlZF9rV2gpLCBjb25zdW1lZF9rV2ggPSBzdW0oY29uc3VtZWRfa1doKSkKCmBgYAoKSW50ZXJlc3RpbmchIFByb2R1Y2VkIGtXaCB3ZW50IGRvd24gYnkgNSUsIGJ1dCBjb25zdW1lZCBrV2ggd2VudCBkb3duIGJ5IDEwJSBkZXNwaXRlIHRoZSBmYWN0IHRoYXQgd2UgZ290IGFuIGVsZWN0cmljIGNhciEgTGV0J3MgZXhwbG9yZSB0aGF0IGZ1cnRoZXIuIAoKYGBge3J9CmRhaWx5X2VsZWN0cmljaXR5IDwtIGhvdXJseV9lbGVjdHJpY2l0eSAlPiUgCiAgZ3JvdXBfYnkoc29sYXJfeWVhciwgeWRheSkgJT4lIAogIHN1bW1hcml6ZShuZXRfa1doID0gc3VtKG5ldF9rV2gpLCBwcm9kdWNlZF9rV2ggPSBzdW0ocHJvZHVjZWRfa1doKSwgY29uc3VtZWRfa1doID0gc3VtKGNvbnN1bWVkX2tXaCkpCgpnZ3Bsb3QoZGFpbHlfZWxlY3RyaWNpdHksIGFlcyh5ZGF5LCBjb25zdW1lZF9rV2gsIGdyb3VwPXNvbGFyX3llYXIsIGNvbG9yPXNvbGFyX3llYXIpKSArIAogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9zbW9vdGgoc3Bhbj0wLjMpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gYygxLCAzMiwgNjAsIDkxLCAxMjEsIDE1MiwgMTgyLCAyMTMsIDI0NCwgMjc0LCAzMDUsIDMzNSksIGxhYmVscyA9IG1vbnRoLmFiYikgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTApKSAKCmBgYAoKCmBgYHtyfQpkYWlseV9lbGVjdHJpY2l0eSA8LSBob3VybHlfZWxlY3RyaWNpdHkgJT4lIAogIG11dGF0ZShzb2xhcl9kYXkgPSBpbnRlcnZhbChhc19kYXRlKGFzLlBPU0lYY3QoJzA0LzE1LzIwMjInLGZvcm1hdD0iJW0vJWQvJVkiLHR6PVN5cy50aW1lem9uZSgpKSksYXNfZGF0ZShkYXRlKSkgLyBkYXlzKDEpIAogICAgICAgICApICU+JSAKICBncm91cF9ieShkYXRlLCBzb2xhcl9kYXkpICU+JSAKICBzdW1tYXJpemUobmV0X2tXaCA9IHN1bShuZXRfa1doKSwgcHJvZHVjZWRfa1doID0gc3VtKHByb2R1Y2VkX2tXaCksIGNvbnN1bWVkX2tXaCA9IHN1bShjb25zdW1lZF9rV2gpKQoKZ2dwbG90KGRhaWx5X2VsZWN0cmljaXR5LCBhZXMoZGF0ZSwgY29uc3VtZWRfa1doKSkgKyAKICBnZW9tX3BvaW50KCkgKwogICNzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gYygxLCAzMiwgNjAsIDkxLCAxMjEsIDE1MiwgMTgyLCAyMTMsIDI0NCwgMjc0LCAzMDUsIDMzNSksIGxhYmVscyA9IG1vbnRoLmFiYikgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTApKSAKCgpgYGAKTG9vayBob3cgb3VyIGVsZWN0cmljaXR5IGNvbnN1bXB0aW9uIGdvdCBtdWNoIGxlc3MgcHJlZGljdGFibGUsIG1vcmUgdW5ldmVuLCBhZnRlciB3ZSBwdXJjaGFzZWQgYW4gRVYgYW5kIGluc3RhbGxlZCBhIGxldmVsIDIgY2hhcmdlciBpbiBsYXRlIEphbnVhcnkvZWFybHkgRmVicnVhcnkgMjAyMyEKCldlIHByb2JhYmx5IG5lZWQgdG8gZmlsdGVyIG91dCB0aGUgRVYgY2hhcmdpbmcgYW5kIGNvbXBhcmUgdGhhdCBzZXBhcmF0ZWx5LiAKCgpgYGB7cn0KCiNob3VybHlfZWxlY3RyaWNpdHkgJT4lIHN1bW1hcml6ZShtaW5fZGF0ZSA9IG1pbihkYXRlKSwgbWF4X2RhdGUgPSBtYXgoZGF0ZSkpCgplbGVjdHJpY2l0eV9ieV90aW1lIDwtIGhvdXJseV9lbGVjdHJpY2l0eSAlPiUgCiAgZmlsdGVyKHNvbGFyX3llYXIgPT0gMSB8IHNvbGFyX3llYXIgPT0gMikgJT4lIAogICNmaWx0ZXIoZGF0ZSA8PSBhcy5QT1NJWGN0KCcxMi8zMS8yMDIyIDAwOjAwJyxmb3JtYXQ9IiVtLyVkLyVZICVIOiVNIix0ej1TeXMudGltZXpvbmUoKSkgKSAlPiUgCiAgZ3JvdXBfYnkodGltZSwgc29sYXJfeWVhcikgJT4lIAogIHN1bW1hcml6ZShwcm9kdWNlZF9rV2ggPSBtZWFuKHByb2R1Y2VkX2tXaCksIGNvbnN1bWVkX2tXaCA9IG1lYW4oY29uc3VtZWRfa1doKSwgbmV0X2tXaCA9IG1lYW4obmV0X2tXaCkpICU+JSAKICBhcnJhbmdlKHRpbWUpCgplbGVjdHJpY2l0eV9ieV90aW1lCgpnZ3Bsb3QoZWxlY3RyaWNpdHlfYnlfdGltZSwgYWVzKHg9dGltZSwgeT1jb25zdW1lZF9rV2gsIGdyb3VwPXNvbGFyX3llYXIsIGNvbG9yPXNvbGFyX3llYXIpKSArCiAgZ2VvbV9wb2ludCgpIAogICNsYWJzKGNvbG91cj0iIix4PSJUaW1lIG9mIERheSIseT0iRWxlY3RyaWNpdHkgQ29uc3VtcHRpb24gKGtXaCkiKSsKICAjc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoInJlZCIsImJsYWNrIiwiZ3JlZW4iKSkgKwogICNnZ3RpdGxlKCJIb21lIEVsZWN0cmljaXR5IGluIDE1LU1pbnV0ZSBJbnRlcnZhbHMgKFNpbmNlIEFwcmlsIDE1LCAyMDIyKSIpCgpgYGAKV293LCBsb29rIGhvdyBtdWNoIGxlc3Mgd2FzIHVzZWQgZHVyaW5nIHRoZSBkYXkgaW4geWVhciAyISEhIExpa2UgYSA1MCUgcmVkdWN0aW9uISAKCklzIHRoaXMgZHVlIHRvIG1pbGRlciB3ZWF0aGVyPyAKCkxldCdzIGxvb2sgYXQgaXQgYnkgbW9udGg6IAoKYGBge3J9CiNlbGVjdHJpY2l0eV9ieV9tb250aCA8LSAKaG91cmx5X2VsZWN0cmljaXR5ICU+JSAKICBtdXRhdGUobW9udGggPSBtb250aChkYXRlKSkgJT4lIAogIGdyb3VwX2J5KHNvbGFyX3llYXIsIG1vbnRoKSAlPiUgCiAgc3VtbWFyaXplKGNvbnN1bWVkX2tXaCA9IG1lYW4oY29uc3VtZWRfa1doKSkgJT4lIAogIGFycmFuZ2UobW9udGgsIHNvbGFyX3llYXIpICU+JSAKICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gbW9udGgsIHZhbHVlc19mcm9tID0gY29uc3VtZWRfa1doKSAlPiUgCiAgYXJyYW5nZShzb2xhcl95ZWFyKQoKI0xvb2sgYXQgbGltaXRlZCB3aW5kb3cgZHVyaW5nIGRheSAobm8gRVYgY2hhcmdpbmcpCiAKIyBob3VybHlfZWxlY3RyaWNpdHkgJT4lIAojICAgZmlsdGVyKHRpbWUgPT0gJzEyOjAwOjAwJykgJT4lIAojICAgI2ZpbHRlcih0aW1lID4gYXMuUE9TSVhjdCgnMDQ6MDA6MDAnLGZvcm1hdD0iJUg6JU06JHMiLHR6PVN5cy50aW1lem9uZSgpKSApICU+JSAgIyYgdGltZSA8IGFzLlBPU0lYY3QoJzE4OjAwJyxmb3JtYXQ9IiVIOiVNIix0ej1TeXMudGltZXpvbmUoKSkpICU+JSAKIyAgIG11dGF0ZShtb250aCA9IG1vbnRoKGRhdGUpKSAlPiUgCiMgICBncm91cF9ieShzb2xhcl95ZWFyLCBtb250aCkgJT4lIAojICAgc3VtbWFyaXplKGNvbnN1bWVkX2tXaCA9IG1lYW4oY29uc3VtZWRfa1doKSkgJT4lIAojICAgYXJyYW5nZShtb250aCwgc29sYXJfeWVhcikgJT4lIAojICAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IG1vbnRoLCB2YWx1ZXNfZnJvbSA9IGNvbnN1bWVkX2tXaCkgJT4lIAojICAgYXJyYW5nZShzb2xhcl95ZWFyKQoKCmBgYAoKTG9vayBhdCBzcGVjaWZpYyBtb250aHMKCmBgYHtyfQplbGVjdHJpY2l0eV9ieV9tb250aF90aW1lIDwtIGhvdXJseV9lbGVjdHJpY2l0eSAlPiUgCiAgbXV0YXRlKG1vbnRoID0gYXNfZmFjdG9yKG1vbnRoKGRhdGUpKSkgJT4lIAogIGZpbHRlcihzb2xhcl95ZWFyID09IDEgfCBzb2xhcl95ZWFyID09IDIpICU+JSAKICBmaWx0ZXIobW9udGggPT0gMTApICU+JSAKICBncm91cF9ieShzb2xhcl95ZWFyLCBtb250aCwgdGltZSkgJT4lIAogIHN1bW1hcml6ZShwcm9kdWNlZF9rV2ggPSBtZWFuKHByb2R1Y2VkX2tXaCksIGNvbnN1bWVkX2tXaCA9IG1lYW4oY29uc3VtZWRfa1doKSwgbmV0X2tXaCA9IG1lYW4obmV0X2tXaCkpICU+JSAKICBhcnJhbmdlKHNvbGFyX3llYXIsIG1vbnRoLCB0aW1lKQoKZ2dwbG90KGVsZWN0cmljaXR5X2J5X21vbnRoX3RpbWUsIGFlcyh0aW1lLCBjb25zdW1lZF9rV2gsIGdyb3VwPXNvbGFyX3llYXIsIGNvbG9yPXNvbGFyX3llYXIpKSArCiAgI2ZhY2V0X2dyaWQocm93cyA9IHZhcnMobW9udGgpKSArCiAgZ2VvbV9wb2ludChzaXplPTAuNSkgKwogIGdndGl0bGUoIkhvbWUgRWxlY3RyaWNpdHkgaW4gMTUtTWludXRlIEludGVydmFscyIpICsKICB5bGFiKCJFbGVjdHJpY2l0eSBDb25zdW1wdGlvbiAoa1doKSIpCgpgYGAKCkV4YW1wbGUgZGF5IHdpdGggRVYgY2hhcmdpbmcKCmBgYHtyfQpzYW1wbGVfZGF5IDwtIGhvdXJseV9lbGVjdHJpY2l0eSAlPiUgCiAgZmlsdGVyKHNvbGFyX3llYXIgPT0gMSB8IHNvbGFyX3llYXIgPT0gMikgJT4lIAogIGZpbHRlcih5ZGF5ID09IDE1NykgJT4lIAogIGdyb3VwX2J5KGRhdGUsIHNvbGFyX3llYXIsIHlkYXksIHRpbWUpICU+JSAKICBzdW1tYXJpemUocHJvZHVjZWRfa1doID0gbWVhbihwcm9kdWNlZF9rV2gpLCBjb25zdW1lZF9rV2ggPSBtZWFuKGNvbnN1bWVkX2tXaCksIG5ldF9rV2ggPSBtZWFuKG5ldF9rV2gpKSAlPiUgCiAgYXJyYW5nZShkYXRlLCBzb2xhcl95ZWFyLCB5ZGF5LCB0aW1lKQoKZ2dwbG90KHNhbXBsZV9kYXksIGFlcyh0aW1lLCBjb25zdW1lZF9rV2gsIGdyb3VwPXNvbGFyX3llYXIsIGNvbG9yPXNvbGFyX3llYXIpKSArCiAgI2ZhY2V0X2dyaWQocm93cyA9IHZhcnMobW9udGgpKSArCiAgZ2VvbV9wb2ludChzaXplPTAuNSkgKwogIGdndGl0bGUoIkhvbWUgRWxlY3RyaWNpdHkgaW4gMTUtTWludXRlIEludGVydmFscyIpICsKICB5bGFiKCJFbGVjdHJpY2l0eSBDb25zdW1wdGlvbiAoa1doKSIpCgpgYGAKCk9rLCBsZXQncyBkZXRlY3QgYW5kIHJlbW92ZSBFViBjaGFyZ2luZyBzZXNzaW9ucy4gCgpgYGB7cn0KIyBsaWJyYXJ5KG9ubGluZUJjcCkKeCA8LSBjKHJub3JtKDEwLCAyLjYsIDAuMiksIHJub3JtKDEsIDIuNi8yLCAwLjIpICsgcm5vcm0oMSwgLjIsIC4xNSksIHJub3JtKDg2LCAuMiwgLjE1KSkKYmNwIDwtIG9ubGluZV9jcCh4KQpzdW1tYXJ5KGJjcCkKCmJjcAoKYGBgCgpgYGB7cn0KIyBsaWJyYXJ5KG9ubGluZUJjcCkKCiNGaWx0ZXIgZGF0YSB0byBmZWVkIHRvIG1vZGVsCnggPC0gc2FtcGxlX2RheSAlPiUgCiAgZmlsdGVyKHNvbGFyX3llYXIgPT0gMikgJT4lIAogIHVuZ3JvdXAoKSAlPiUgCiAgc2VsZWN0KGNvbnN1bWVkX2tXaCkKCgp4IDwtIHgkY29uc3VtZWRfa1doCgoKYmNwIDwtIG9ubGluZV9jcCh4KQoKc3VtbWFyeShiY3ApCgpiY3AKCnBsb3Qoc3VtbWFyeShiY3ApKQoKIyBnZ3Bsb3Qoc2FtcGxlX2RheSwgYWVzKHRpbWUsIGNvbnN1bWVkX2tXaCwgZ3JvdXA9c29sYXJfeWVhciwgY29sb3I9c29sYXJfeWVhcikpICsKIyAgICNmYWNldF9ncmlkKHJvd3MgPSB2YXJzKG1vbnRoKSkgKwojICAgZ2VvbV9wb2ludChzaXplPTAuNSkgKwojICAgZ2d0aXRsZSgiSG9tZSBFbGVjdHJpY2l0eSBpbiAxNS1NaW51dGUgSW50ZXJ2YWxzIikgKwojICAgeWxhYigiRWxlY3RyaWNpdHkgQ29uc3VtcHRpb24gKGtXaCkiKQoKCmBgYAoKCg==